package cc.cc4414.spring.mybatis.update;

import cc.cc4414.spring.common.observer.BaseObservable;
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda;
import org.apache.ibatis.reflection.property.PropertyNamer;
import org.springframework.stereotype.Service;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;

/**
 * 用于冗余字段更新的被观察者
 *
 * @author cc 2021年04月07日
 */
@Service
public class UpdateObservable {
    private final Map<String, BaseObservable> map = new ConcurrentHashMap<>();

    /**
     * 根据topic获取被观察者
     *
     * @param topic 主题
     * @return 被观察者
     */
    public BaseObservable getObservable(String topic) {
        return map.computeIfAbsent(topic, k -> new BaseObservable());
    }

    /**
     * 根据表名和字段名获取被观察者
     *
     * @param tableName 表名
     * @param fieldName 字段名
     * @return 被观察者
     */
    public BaseObservable getObservable(String tableName, String fieldName) {
        return getObservable(tableName + ":" + fieldName);
    }

    /**
     * 从func获取表名和字段名，然后获取被观察者
     *
     * @param func 表名和字段名
     * @return 被观察者
     */
    public BaseObservable getObservable(SFunction<?, ?> func) {
        SerializedLambda lambda = LambdaUtils.resolve(func);
        String tableName = lambda.getImplClassName();
        String fieldName = PropertyNamer.methodToProperty(lambda.getImplMethodName());
        return getObservable(tableName, fieldName);
    }

    /**
     * 新增观察者
     *
     * @param func     表名和字段名
     * @param consumer 需要执行的方法
     * @param <T>      实体类型
     */
    public <T> void addObserver(SFunction<T, ?> func, Consumer<T> consumer) {
        BaseObservable observable = getObservable(func);
        observable.addObserver(o -> {
            T entity = UpdateUtils.getEntity(func, o);
            if (entity == null) {
                return;
            }
            consumer.accept(entity);
        });
    }
}
